/*!
 Temelia - Hash Map samples.

 Copyright (C) 2008 Ceata (http://ceata.org/proiecte/temelia).

 @author Dascalu Laurentiu

 This program is free software; you can redistribute it and
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 3
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "include/samples.h"
#include <temelia/hash_map.h>
#include <temelia/linked_list.h>
#include <temelia/pair.h>
#include <temelia/red_black_tree.h>

static void hash_map_handler(void *key, void *value, void *fout)
{
	PRINT("%d = %d\n", *(int *) key, *(int *) value);
}

static void hash_map_multiple_types_handler(void *key, void *context)
{
	if (!pair_get_key(context))
	{
		// key is a hash map key
		PRINT("%d ", hash_map_contains_key(pair_get_value(context), key,
				int_compare, NULL));
	}
	else
	{
		// key is a hash map value
		PRINT("%d ", hash_map_contains_value(pair_get_value(context), key,
				int_compare, NULL));
	}
}

void pair_level_handler(void *key, int level, void *fout)
{
	int i;
	for (i = 0; i < level; i++)
		PRINT("   ");
	if (key == NULL)
		PRINT("(NULL, NULL)\n");
	else
		PRINT("(%d, %d)\n", *(int *) pair_get_key(key),
				*(int *) pair_get_value(key));
}

static void sample1()
{
	int **keys, **values, i, N = 50;
	void *result;
	hash_map_t map;

	PRINT("\n\n Sample 1 started \n\n");

	map = hash_map_new();

	keys = int_array_new(N, N / 2);
	values = int_array_new(N, N / 2);

	for (i = 0; i < N; i++)
	{
		result = hash_map_put(map, keys[i], values[i], int_compare, NULL);
		if (!result)
			PRINT("Added : ");
		else
			PRINT("Item already exists, changing it's value : ");
		PRINT("%d %d\n", *(int *) keys[i], *(int *) values[i]);
	}

	PRINT("Hash map size %d, empty : %d\n", hash_map_get_size(map),
			hash_map_is_empty(map));

	PRINT("Printing the hash_map ... \n");
	red_black_tree_show_indented(hash_map_get_internal_representation(map),
			pair_level_handler, NULL);

	hash_map_clear(map);

	PRINT("Hash map cleared, printing again ... \n");
	red_black_tree_show_indented(hash_map_get_internal_representation(map),
			pair_level_handler, NULL);

	int_array_delete(keys, N);
	int_array_delete(values, N);

	hash_map_delete(map);

	PRINT("\n\n Sample 1 ended \n\n");
}

static void sample2()
{
	int **keys, **values, i, N = 50;
	hash_map_t map;

	PRINT("\n\n Sample 2 started \n\n");

	map = hash_map_new();

	keys = int_array_new(N, N / 4);
	values = int_array_new(N, N / 4);

	for (i = 0; i < N; i++)
		hash_map_put(map, keys[i], values[i], int_compare, NULL);

	PRINT("%d ", hash_map_contains_key(map, keys[5], int_compare, NULL));
	PRINT("%d ", hash_map_contains_key(map, values[10], int_compare, NULL));
	PRINT("%d ", hash_map_contains_value(map, values[5], int_compare, NULL));
	PRINT("%d ", hash_map_contains_value(map, keys[10], int_compare, NULL));

	PRINT("\nThe values are : \n");
	red_black_tree_show_indented(hash_map_get_internal_representation(map),
			pair_level_handler, NULL);

	PRINT("\nThe keys are : \n");
	red_black_tree_show_indented(hash_map_get_internal_representation(map),
			pair_level_handler, NULL);

	PRINT("\nPrinting the hash map ...\n");
	red_black_tree_show_indented(hash_map_get_internal_representation(map),
			pair_level_handler, NULL);

	PRINT("\nRemoving some keys ...\n");

	for (i = 0; i < N / 2 - 1; i++)
	{
		PRINT("Eliminating key %d\n", keys[2 * i][0]);
		hash_map_remove_key(map, keys[2 * i], int_compare, NULL);
		PRINT("Eliminating value %d\n", values[2 * i + 1][0]);
		hash_map_remove_value(map, values[2 * i + 1], int_compare, NULL);

		PRINT("\nPrinting the hash map ...\n");
		red_black_tree_show_indented(hash_map_get_internal_representation(map),
				pair_level_handler, NULL);
		PRINT("\nSize %d\n", hash_map_get_size(map));
	}

	int_array_delete(keys, N);
	int_array_delete(values, N);

	hash_map_delete(map);

	PRINT("\n\n Sample 2 ended \n\n");
}

static void sample3()
{
	hash_map_t map;
	int i, key, value;
	pair_t context;

	map = hash_map_new();

	for (i = 0; i < 10; i++)
	{
		key = rand() % 1000;
		value = rand() % 1000;
		PRINT("Inserez (%d, %d)\n", key, value);
		hash_map_put(map, int_new(key), int_new(value), int_compare, NULL);
	}

	red_black_tree_show_indented(hash_map_get_internal_representation(map),
			pair_level_handler, NULL);

	PRINT("\nKeys\n");
	hash_map_iterate_keys(map, int_handler, NULL);
	PRINT("\n");

	PRINT("Values\n");
	hash_map_iterate_values(map, int_handler, NULL);
	PRINT("\n");

	/*
	 * Activate nested tree traversing.
	 */
	temelia_init();

	PRINT("Searching keys\n");
	context = pair_new(NULL, map);
	hash_map_iterate_keys(map, hash_map_multiple_types_handler, context);
	PRINT("\n");

	PRINT("Searching values\n");
	pair_set_key(context, (void *) 0x1);
	hash_map_iterate_values(map, hash_map_multiple_types_handler, context);
	PRINT("\n");

	hash_map_iterate(map, hash_map_handler, NULL);
	hash_map_iterate_keys(map, int_delete, NULL);
	hash_map_iterate_values(map, int_delete, NULL);
	pair_delete(context);
	hash_map_delete(map);
	temelia_finish();
}

void run_hash_map_samples()
{
	sample1();
	sample2();
	sample3();
}
